import Lexer from './Lexer'
import Part from './Part'
import Cell from './Cell'
import Measure from './Measure'
import { range } from '../utils/helpers'

/**
 * Body类表示一个音乐作品的身体部分，包含一个或多个音乐部分（Part）
 * 该类负责解析、填充小节、创建措施并存储音乐数据
 */
export default class Body {
  /**
   * 构造函数用于创建一个Body实例
   * @param {Object} body - 包含音乐部分的对象，可选参数，默认为一个空的部分数组
   */
  constructor(body = { parts: [] }) {
    this.name = 'body' // 定义名称为“body”

    // 根据传入的body类型，决定解析方式
    if (body.name === 'lexer') {
      this.parse(body) // 解析传入的lexer对象
    } else if (typeof body === 'string') {
      this.parse(new Lexer(body)) // 将字符串解析为音乐部分
    } else {
      if (!body.parts) body.parts = [] // 确保parts属性存在
      this.parts = body.parts.map(part => new Part(part)) // 将每个部分对象转换为Part实例
    }

    this.fillStaves() // 填充小节
    this.makeMeasures() // 创建措施
  }

  /**
   * 解析函数，用于解析Lexer对象生成音乐部分
   * @param {Object} lexer - Lexer对象，包含音乐语法信息
   */
  parse(lexer) {
    this.parts = [] // 初始化部分数组
    let part

    do {
      part = new Part(lexer) // 创建一个新的音乐部分
      if (!part.isEmpty) {
        this.parts.push(part) // 如果部分不为空，则加入到parts数组中
      }
    } while (lexer.is('part-head')) // 继续解析直到达到部分头部
  }

  /**
   * 遍历每个小节并执行给定的回调函数
   * @param {Function} cb - 回调函数，接收三个参数：staff, s, p
   */
  eachStaff(cb) {
    this.parts.forEach((part, p) => {
      part.staves.forEach((staff, s) => cb(staff, s, p))
    })
  }

  /**
   * 对每个小节执行给定的回调函数，并返回结果数组
   * @param {Function} cb - 回调函数，接收三个参数：staff, s, p
   * @returns {Array} - 回调函数的执行结果数组
   */
  mapStaff(cb) {
    const result = []
    this.eachStaff((staff, s, p) => result.push(cb(staff, s, p)))
    return result
  }

  /**
   * 填充小节函数，确保所有小节有相同数量的cells
   */
  fillStaves() {
    const maxLen = Math.max.apply(null, this.mapStaff(staff => staff.cells.length)) // 获取最长的小节长度

    if (maxLen <= 0) {
      return
    }

    this.eachStaff(staff => {
      if (staff.cells.length === maxLen) return // 如果小节长度已经是最长，则无需填充
      const m = maxLen - staff.cells.length // 计算需要填充的cells数量

      staff.cells = staff.cells.concat(range(m).map(() => new Cell())) // 填充cells到最大长度
    })
  }

  /**
   * 创建措施函数，用于将cells分组为措施
   */
  makeMeasures() {
    if (!this.parts.length) {
      this.measures = []
      return
    }

    const m = this.parts[0].staves[0].cells.length // 获取第一个小节的cells数量
    const measures = range(m).map(() => ({ parts: [] })) // 创建措施数组

    this.eachStaff((staff, s, p) => {
      measures.forEach((measure, m) => {
        measure.parts[p] = measure.parts[p] || {} // 确保每个部分存在
        const mpart = measure.parts[p]
        mpart.staves = mpart.staves || []
        mpart.staves[s] = staff.cells[m] // 将cells分配到措施中
      })
    })

    this.measures = measures.map(measure => new Measure(measure)) // 将措施对象转换为Measure实例
  }

  /**
   * 转换Body实例为字符串表示
   * @returns {String} - Body实例的字符串表示
   */
  toString() {
    if (this.parts.length === 1) {
      return this.parts[0].singlePartToString() // 如果只有一个部分，调用单部分转换函数
    }

    return this.parts.join('\n\n') // 否则，将所有部分连接为一个字符串
  }

  /**
   * 将Body实例转换为JSON格式
   * @returns {Object} - Body实例的JSON表示
   */
  toJSON() {
    const { parts } = this
    return { parts } // 返回parts的JSON表示
  }
}
